package service

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"gitee.com/openeuler/PilotGo/sdk/logger"
	jobstatus "openeuler.org/PilotGo/PilotGo-plugin-automation/internal/module/common/enum/job"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/internal/module/common/exec"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/internal/module/job_history/dao"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/internal/module/job_history/model"
	workflow "openeuler.org/PilotGo/PilotGo-plugin-automation/internal/module/job_workflow/dao"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/pkg/response"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/pkg/utils"
)

func CreateJob(data *model.JobDTO) (string, error) {
	jobId := utils.GenerateUUID()
	workflowName, tag, err := workflow.GetTagEnNameByWorkflowId(data.Job.WorkflowId)
	if err != nil {
		return jobId, err
	}

	jobName := getJobName(workflowName)
	if err := dao.CreateJob(jobId, jobName, data); err != nil {
		return jobId, err
	}

	go saveSubJobResult(jobId, data)

	if err := execJob(jobId, tag); err != nil {
		return jobId, err
	}

	// 将作业信息缓存到Redis
	if err := SetSubJobCache(jobId, strings.Split(data.Job.ExecHosts, ","), jobstatus.SubJobNotRun, 0, len(data.SubJobs)); err != nil {
		logger.Error("缓存子作业信息失败: jobId=%s, err=%v", jobId, err)
		return jobId, err
	}
	if err := SetJobCache(jobId, len(strings.Split(data.Job.ExecHosts, ",")), 0); err != nil {
		logger.Error("缓存作业信息失败: jobId=%s, err=%v", jobId, err)
		return jobId, err
	}

	return jobId, nil
}

func getJobName(workflowName string) string {
	timestamp := time.Now().Format("20060102150405")
	return workflowName + "_" + timestamp
}

func GetJobs(query *response.PagedQuery, jobType string) ([]*model.Job, int, error) {
	return dao.GetJobs(query, jobType)
}

func GetJobByJobId(jobId string) (interface{}, error) {
	return dao.GetJob(jobId)
}

func GetJobResultByJobId(jobId string) (interface{}, error) {
	job, err := dao.GetJobResult(jobId)
	if err != nil {
		return nil, fmt.Errorf("获取作业状态失败: jobId=%s, err=%v", jobId, err.Error())
	}
	return job, nil
}

func GetSubJobResultByJobId(jobId, ip string) (interface{}, error) {
	subJobResult, err := dao.GetSubJobResult(jobId, ip)
	if err != nil {
		return nil, fmt.Errorf("获取%s结果失败: jobId=%s, err=%v", ip, jobId, err.Error())
	}
	currentStepResult, stepResults, err := dao.GetSubJobStepResult(jobId, ip, subJobResult.CurrentStep)
	if err != nil {
		return nil, fmt.Errorf("获取%s stepNum=%v 结果失败: jobId=%s, err=%v", ip, subJobResult.CurrentStep, jobId, err.Error())
	}

	var result model.Result
	if len(currentStepResult.Result) > 0 {
		if err := json.Unmarshal(currentStepResult.Result, &result); err != nil {
			return nil, fmt.Errorf("解析 stepNum=%v Result字段失败: jobId=%s, err=%v", subJobResult.CurrentStep, jobId, err.Error())
		}
	}

	resultMap := make(map[string]interface{})
	resultMap["id"] = subJobResult.ID
	resultMap["job_id"] = subJobResult.JobId
	resultMap["workflow_id"] = subJobResult.WorkflowId
	resultMap["ip"] = subJobResult.IP
	resultMap["current_step"] = subJobResult.CurrentStep
	resultMap["exec_code"] = subJobResult.ExecCode
	resultMap["sub_job_status"] = subJobResult.SubJobStatus
	resultMap["start_time"] = subJobResult.StartTimeStr
	resultMap["end_time"] = subJobResult.EndTimeStr
	resultMap["results"] = stepResults
	resultMap["message"] = result.Stderr

	return resultMap, nil
}

func ResumeManualJob(resumeReq *model.TaskResumeRequest) error {
	var targetIPs []string

	if resumeReq.IP != "" {
		targetIPs = []string{resumeReq.IP}
	} else {
		job, err := dao.GetJob(resumeReq.JobId)
		if err != nil {
			return err
		}
		targetIPs = strings.Split(job.Job.ExecHosts, ",")
	}

	for _, ip := range targetIPs {
		if err := exec.ResumeManualJob(ip, resumeReq); err != nil {
			logger.Error("主机%s %s", ip, err.Error())
		}
	}

	return nil
}

func execJob(jobId string, tag string) error {
	job, err := dao.GetJob(jobId)
	if err != nil {
		return err
	}

	for _, ip := range strings.Split(job.Job.ExecHosts, ",") {
		if err := exec.ExecJob(ip, toJobTask(job, tag)); err != nil {
			return err
		}
	}
	return nil
}

func toJobTask(dto *model.JobDTO, tag string) exec.JobTask {
	job := exec.Job{
		JobId:         dto.Job.JobId,
		OperationType: dto.Job.OperationType,
		JobType:       dto.Job.JobType,
		ExecCode:      dto.Job.ExecCode,
		JobStatus:     dto.Job.JobStatus,
		Tag:           tag,
		UsageType:     dto.Job.UsageType.String(),
	}

	var inputParams []exec.JobParams
	for _, param := range dto.InputParams {
		inputParams = append(inputParams, exec.JobParams{
			JobId: param.JobId,
			Name:  param.Name,
			Type:  param.Type,
			Value: param.Value,
		})
	}

	var outputParams []exec.JobOutputParams
	for _, param := range dto.OutputParams {
		outputParams = append(outputParams, exec.JobOutputParams{
			JobId: param.JobId,
			Name:  param.Name,
			Type:  param.Type,
			Value: param.Value,
		})
	}

	var subjobs []exec.SubJob
	for _, subjob := range dto.SubJobs {
		subjobs = append(subjobs, exec.SubJob{
			JobId:         subjob.JobId,
			ScriptId:      subjob.ScriptId,
			StepNum:       subjob.StepNum,
			StepType:      subjob.StepType.String(),
			ScriptType:    subjob.ScriptType,
			ScriptParam:   subjob.ScriptParam,
			ScriptContent: utils.EncodeScriptContent(subjob.ScriptContent),
			ScriptTimeout: subjob.ScriptTimeout,
		})
	}

	return exec.JobTask{
		Job:          job,
		InputParams:  inputParams,
		OutputParams: outputParams,
		SubJobs:      subjobs,
	}
}
